<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Class 的基本语法</title>
</head>

<body>
    <script>
        // ES6的class可以看做是一个语法糖，她的绝大部分功能，ES5都可以做到，新的class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已

        // 原来使用构造方法
        function Point(x, y) {
            this.x = x;
            this.y = y;
        }
        Point.prototype.toString = function () {
            return '(' + this.x + ',' + this.y + ')'
        }
        var p = new Point(1, 2)
        console.log(p.toString())

        //使用class 类
        class Point2 {
            constructor(x, y) {
                this.x = x
                this.y = y;

            }
            toString() {
                return '(' + this.x + ',' + this.y + ')'
            }
        }
        var p2 = new Point2(3, 4)
        console.log(p2.toString())
        // ES6 的类 ，完全可以看做构造函数的另一种写法
        console.log(typeof Point2) // "function"
        console.log(Point2 === Point2.prototype.constructor) // true
        //以上表明 类的数据类型就是函数  类本身就是指向构造函数
        console.log(p2)
        // 构造函数的prototype 属性，在ES6 的 ‘类’上面继续存在，事实上类的所有方法都定义在类的propterty 属性上限 
        class Point3 {
            constructor() {

            }
            toString() {

            }
            toValue() {

            }
        }
        // 以上等同于
        Point3.prototype = {
            constructor() {},
            toString() {},
            toValue() {}
        }

        // 因此，在类的实例上面调用方法，其实就是调用原型上的方法
        class B {}
        const b = new B()
        console.log(b.constructor === B.prototype.constructor)

        //类本身指向原型上的构造函数
        // 类实例的方法其实也是原型上的方法

        // 由于类的方法定义在prototype 对象上面，所以类的新方法可以添加在prototype 对象上面，Object.assign()方法可以很方便的一次向类添加多个方法

        class Point4 {
            constructor() {}
        }
        Object.assign(Point.prototype, {
            toString() {},
            toValue() {}
        })

        // 类的内部定义的所有方法都是不可枚举的（non-enumerable）
        console.log(Object.keys(Point3.prototype))
        console.log(Object.getOwnPropertyNames(Point3.prototype))

        console.log('===构造方法===')
        console.log(Object.keys(Point.prototype))
        console.log(Object.getOwnPropertyNames(Point.prototype))

        //与ES5 一样，实例的属性除非显示定义在其本身，否则都是定义在原型上
        // var point2 = new Point2(5, 6)
        console.log(p2.hasOwnProperty('x')) //true
        console.log(p2.hasOwnProperty('y')) //true
        console.log(p2.hasOwnProperty('toString')) //false
        console.log(p2.__proto__.hasOwnProperty('toString')) //true

        // 与ES5 一样 所有实例共享一个原型对象
        var pp = new Point2(33, 44)
        var pp2 = new Point2(88, 99)
        console.log(pp.__proto__ === pp2.__proto__) // true


        // 取值函数（getter）和存值函数（setter） §


        // 使用Class 表达式可以立即执行class
        let person = new class {
            constructor(name) {
                this.name = name;
            }

            sayName() {
                console.log(this.name);
            }
        }('张三');
        person.sayName()

        // 注意点
        // 1、严格模式  类和模块的内部默认就是严格模式，不需要使用 use strict 
        // 2、不存在提升 类不存在变量提升

        // new Foo()// ReferenceError
        // class Foo {}


        // this 指向问题
        /**
         * 类方法如果内部含有this,他默认指向类的实例，但是使用时需要注意,一旦单独使用该方法，可能会报错
         * 
         * 
         * 
         */
        class Logger {
            printName(name = 'there') {
                this.print(`Hello ${name}`)
            }
            print(text) {
                console.log(text);
            }
        }
        const logger = new Logger();
        const {
            printName
        } = logger;
        // printName() // Class 的基本语法.html:133 Uncaught TypeError: Cannot read property 'print' of undefined

        // 上面代码中，printName 方法中的this,默认指向Logger 类的实例，但是，如果将这个方法提取出来单独使用，this 会指向该方法运行时所在的环境（由于class 内部）是严格模式，所以this 的实际指向是undefined，因此导致point 方法报错
        // 怎么改进?
        // 1 绑定this
        class Logger1 {
            constructor() {
                this.printName = this.printName.bind(this)
            }
        }
        //2、使用箭头函数
        class Obj {
            constructor() {
                this.getThis = () => this;
            }
        }
        const myObj = new Obj()
        console.log(myObj.getThis() === myObj)

        // 3 使用Proxy 获取方法的时候自动绑定this



        //静态方法
    </script>
</body>

</html>